/**
 * Created with IntelliJ IDEA.
 * User: alex
 * Date: 01.11.13
 * Time: 14:13
 * SVN $Id: MainFrameFXMLController.java 74 2013-12-24 09:39:37Z kuzn2k@gmail.com $
 */

package study.alex.resumedb;

import extfx.scene.control.CalendarView;
import extfx.scene.control.DatePicker;
import extfx.scene.control.skin.DatePickerSkin;
import javafx.application.Platform;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontSmoothingType;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.StringConverter;
import org.controlsfx.control.action.Action;
import org.controlsfx.dialog.Dialog;
import org.controlsfx.dialog.Dialogs;
import org.jpedal.PdfDecoder;
import org.jpedal.exception.PdfException;
import study.alex.resumedb.jdo.*;

import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Transaction;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.chrono.Chronology;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import extfx.scene.control.DatePicker;
import extfx.scene.control.CalendarView;



public class MainFrameFXMLController implements Initializable {

    private static Stage stage = null;
    private static Stage addResumeStage = null;
    private static Stage sentResumeDialog = null;
    private static Stage showResumeStatusDialog =  null;
    private static MainFrameFXMLController controller = null;
    private static Mine mine = null;
    private static ConcurrentMap<Long, Contact> catalog = null;
    private static ConcurrentMap<Long, Resume> resumes = null;
    private static ConcurrentMap<Long, List<resumeTracker>> history = null;
    private static PersistenceManagerFactory pmf = null;
    private static boolean changed = false;
    private static Contact currentContact = null;
    private static long curContactId = 0;
    private static Resume currentResume = null;
    private static long curResumeId = 0;
    private static FileChooser fileChooser = new FileChooser();
    private static File currentDirectoryPath = null;
    private static File fileResume = null;
    private static BlockingQueue<pipeMessage> pipe = null;
    private static Thread prevPreviewThread = null;

    @FXML
    public ResourceBundle resources;

    @FXML
    public URL location;

    @FXML
    public static ListView<resumeTracker> currentResumeHistoryList;
//    @FXML
//    private TableColumn<ResumeViewItem, Long> emplTableIdCol;
//    @FXML
//    private TableColumn<ContactTableItem, String> emplTableNameCol;
//    @FXML
//    private TableColumn<ContactTableItem, String> emplTableOrgCol;

    @FXML
    public static TextArea historyContactDetailText;
    @FXML
    public static Label resumeInfoLabel;

    @FXML
    public static TableView<ResumeViewItem> resumeListView;
    @FXML
    public static TableColumn<ResumeViewItem, Long> resumeTableIdCol;
    @FXML
    public static TableColumn<ResumeViewItem, String> resumeTableSpecCol;
    @FXML
    public static TableColumn<ResumeViewItem, String> resumeTableTypeCol;
    @FXML
    public static TableColumn<ResumeViewItem, Integer> resumeTableSalaryCol;


    @FXML
    public static VBox resumePreviewPane;

    @FXML
    public static TextField myName;
    @FXML
    public static TextField myAddress;
    @FXML
    public static TextField myPhone;
    @FXML
    public static TextField myEmail;
    @FXML
    public static Tab tabMyData;

    @FXML
    public static TextField detAddress;
    @FXML
    public static TextField detEmail;
    @FXML
    public static TextField detName;
    @FXML
    public static TextField detOrganization;
    @FXML
    public static CheckBox detOrganizationFlag;
    @FXML
    public static TextField detPhone;
    @FXML
    public static TextArea detComment;
    @FXML
    public static Label statusbar;
    @FXML
    private static Label DetailLabel;

    @FXML
    public static TableView<ContactTableItem> emplTable;
    @FXML
    public static TableColumn<ContactTableItem, Long> emplTableIdCol;
    @FXML
    public static TableColumn<ContactTableItem, String> emplTableNameCol;
    @FXML
    public static TableColumn<ContactTableItem, String> emplTableOrgCol;

    @FXML
    public static TableView<ContactTableItem> addResumeContactTableView;

    @FXML
    public static TableColumn<ContactTableItem, String> addResumeEmailCol;

    @FXML
    public static TableColumn<ContactTableItem, Long> addResumeIdCol;

    @FXML
    public static TableColumn<ContactTableItem, String> addResumeNameCol;
    @FXML
    public static TableColumn<ContactTableItem, String> addResumePhoneCol;
    @FXML
    public static TableView<ContactTableItem> sentResumeContactTableView;

    @FXML
    public static TableColumn<ContactTableItem, String> sentResumeEmailCol;

    @FXML
    public static TableColumn<ContactTableItem, Long> sentResumeIdCol;

    @FXML
    public static TableColumn<ContactTableItem, String> sentResumeNameCol;
    @FXML
    public static TableColumn<ContactTableItem, String> sentResumePhoneCol;
    @FXML
    public static TextArea sentComment;
    @FXML
    public static extfx.scene.control.DatePicker sentDate;
    @FXML
    public static extfx.scene.control.DatePicker dateConfirm;
    @FXML
    public static Label ResumeContactInfo;


    @FXML
    public static TextField addResumePath;

    @FXML
    public static TextField addResumeSalary;

    @FXML
    public static TextField addResumeSpec;
    @FXML
    public static Label addResumeContactInfo;
    @FXML
    public static Button buttonAddResume;
    @FXML
    public static CheckBox fileUpdateFlag;
    @FXML
    public static Button buttonSentResume;
    @FXML
    public static Button buttonCloseSentResumeDialog;
    private static Map<Long, resumeTracker> currentTrackerList = null;
    private static resumeTracker currentTracker = null;

    @FXML
    public static Label employerInfo;
    @FXML
    public static extfx.scene.control.DatePicker sentDateField;
    @FXML
    public static extfx.scene.control.DatePicker lastCallField;
    @FXML
    public static extfx.scene.control.DatePicker interviewDateField;
    @FXML
    public static ChoiceBox<resumeStatus> statusField;
    @FXML
    public static TextArea commentArea;




    @FXML
    private void sentResumeDoAction(ActionEvent event) {
        ObservableList<ContactTableItem> selectedContact = sentResumeContactTableView.getSelectionModel().getSelectedItems();
        if (selectedContact == null
                || selectedContact.size() < 1) {
            Dialogs dg = Dialogs.create();
            dg.owner(sentResumeDialog);
            dg.title("Ошибка при регистрации отправки");
            dg.masthead("Нужно выбрать контактные данные для резюме");

            Action response = dg.showError();
            return;

        }
        GregorianCalendar today =  new GregorianCalendar();
        GregorianCalendar confirm = new GregorianCalendar();
        confirm.add(GregorianCalendar.DAY_OF_MONTH, resumeTracker.CONFIRM_CALL_AFTER);
        if (sentDate.getValue() == null) {
            Date todayDate = new Date(today.getTimeInMillis());
            sentDate.setValue(todayDate);
        } else {
            today.setTime(sentDate.getValue());
        }
        if (dateConfirm.getValue() == null) {
            Date confirmDate = new Date(confirm.getTimeInMillis());
            dateConfirm.setValue(confirmDate);
        } else {
            confirm.setTime(dateConfirm.getValue());
        }

        if (currentResume == null) {
            Dialogs dg = Dialogs.create();
            dg.owner(sentResumeDialog);
            dg.title("Ошибка при регистрации отправки");
            dg.masthead("Не выбрано резюме");

            Action response = dg.showError();
            return;
        }

        for (ContactTableItem  item : selectedContact) {
            resumeTracker tracker = new resumeTracker(catalog.get(item.getId()), today, confirm, currentResume.getId());
            tracker.setComment(sentComment.getText());
            tracker.setStatus(resumeStatus.UNCHECKED);
            if (history.get(currentResume.getId()) == null || history.get(currentResume.getId()).size() < 1) history.put(currentResume.getId(), new LinkedList<resumeTracker>());
            history.get(currentResume.getId()).add(tracker);
            saveTracker(tracker);
        }
        setStatusbar("Отправка зарегистрирована");
    }

    @FXML
    private void closeSentResumeWindow() {

        fillResumeHistoryDetail(currentResume);
        sentResumeDialog.hide();

    }

    @FXML
    private void closeShowResumeStatusWindow() {

        fillResumeHistoryDetail(currentResume);
        showResumeStatusDialog.hide();
    }
    @FXML
    private void updateResumeStatus() {

        if (lastCallField.getValue() != null) {
            Date date = lastCallField.getValue();
            GregorianCalendar greDate = new GregorianCalendar();
            greDate.setTime(date);
            currentTracker.setLastCall(greDate);
        }
        if (statusField.getValue() == resumeStatus.ACCEPTED)
            if (interviewDateField.getValue() != null) {
                currentTracker.setStatus(statusField.getValue());
                Date date = interviewDateField.getValue();
                GregorianCalendar greDate = new GregorianCalendar();
                greDate.setTime(date);
                currentTracker.setDateOfInterview(greDate);
            } else {
                setStatusbar("Не указана дата собеседования, использую завтрашнюю дату");
                GregorianCalendar date = new GregorianCalendar();
                date.add(GregorianCalendar.DAY_OF_MONTH, 1);
                currentTracker.setDateOfInterview(date);
            }
        else currentTracker.setStatus(statusField.getValue());
        currentTracker.setComment(commentArea.getText());
        fillHistoryDetail(currentTracker);
        if (!saveObject(currentTracker, stage)) setStatusbar("Данные не сохранены в базу данных, перед выходом - необходимо сохранение через меню");

        showResumeStatusDialog.hide();
    }


    @FXML
    private void addResumeDoAction(ActionEvent event) {

        if (fileResume == null) {

            Dialogs dg = Dialogs.create();
            dg.owner(addResumeStage);
            dg.title("Ошибка при добавлении резюме");
            dg.masthead("Нужно выбрать файл резюме");

            Action response = dg.showError();
            return;
        }
        if (addResumeContactTableView.getSelectionModel().getSelectedItem() == null) {
            Dialogs dg = Dialogs.create();
            dg.owner(addResumeStage);
            dg.title("Ошибка при добавлении резюме");
            dg.masthead("Нужно выбрать контактные данные для резюме");

            Action response = dg.showError();
            return;

        }
        if (addResumeSalary.getText().equals("")) addResumeSalary.setText("0");

        Integer salary;
        try {
            salary = Integer.parseInt(addResumeSalary.getText());
        } catch (NumberFormatException e) {
            Dialogs dg = Dialogs.create();
            dg.owner(addResumeStage);
            dg.title("Ошибка при добавлении резюме");
            dg.masthead("Значение заработной платы должно быть числом.  Ноль означает - \"Зарплата по договоренности\"");

            Action response = dg.showError();
            return;
        }

        setStatusbar("Чтение файла");
        Resume newResume = null;
        try {
            assert addResumeContactTableView.getSelectionModel().getSelectedItem() != null : "Всегда должны быть выбраны контактные данные";

            Contact ct = catalog.get(addResumeContactTableView.getSelectionModel().getSelectedItem().getId());

            assert ct != null : "Выбранный контакт должен существовать в каталоге";

            newResume = new Resume(ct, salary, addResumeSpec.getText(), fileResume);
            setStatusbar("Файл прочитан");
        } catch (IOException e) {
            Dialogs dg = Dialogs.create();
            dg.owner(addResumeStage);
            dg.title("Ошибка при добавлении резюме");
            dg.masthead("Сбой при выполении файловой операции при чтении");
            dg.message("Попробуйте еще раз");

            Action response = dg.showError();
            setStatusbar("Ошибка при чтении файла");
        }

        if (newResume != null)
            if (saveResume(newResume)) {
                resumes.put(newResume.getId(), newResume);
                resumeListView.getItems().add(new ResumeViewItem(newResume.getId(), newResume.getPosition(), newResume.getSalary(), newResume.getType()));
                setStatusbar("Новое резюме добавлено");
                refreshResumeTableView();
                closeAddResumeWindow();
            }
            else setStatusbar("Ошибка базы данных при добавлении резюме");
    }

    private boolean saveResume(Resume newResume) {
        return saveObject(newResume, addResumeStage);
    }
    private boolean saveTracker(resumeTracker tracker) {
        return saveObject(tracker, sentResumeDialog);
    }

    private boolean saveObject(Object newObject, Stage ownerWindow) {
        boolean success = true;

        if (newObject == null) return false;

        PersistenceManager pm = pmf.getPersistenceManager();

        setStatusbar("Сохранение в базе данных");

        Transaction tx = pm.currentTransaction();
        try {

            tx.begin();

            pm.makePersistent(newObject);
            tx.commit();
        }
        finally {
            if (tx.isActive())
            {
                tx.rollback();

                Dialogs dg = Dialogs.create();
                dg.owner(ownerWindow);
                dg.title("Ошибка доступа к базе данных");
                dg.masthead("Не удается сохранить данные");
                dg.message("Попробуйте еще раз");

                Action response = dg.showError();

                success = false;

            }
            pm.close();
            setStatusbar("Завершено");
        }

        return success;
    }


    @FXML
    private void addResumePathSelect(ActionEvent event) {

        if (currentDirectoryPath!= null) fileChooser.setInitialDirectory(currentDirectoryPath);
        else fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
        fileChooser.setTitle("Выберите файл с резюме");
        fileChooser.getExtensionFilters().setAll(
//                new FileChooser.ExtensionFilter("Все файлы", "*.*"),
                new FileChooser.ExtensionFilter("PDF", "*.pdf"),
                new FileChooser.ExtensionFilter("DOC", "*.doc", "*.docx"));
        fileResume = fileChooser.showOpenDialog(addResumeStage);

        if (fileResume != null && fileResume.isFile()) {

            try {
                addResumePath.setText(fileResume.getCanonicalPath());
                currentDirectoryPath = fileResume.getParentFile();
            } catch (IOException e) {
                fileResume = null;
                addResumePath.setText("");
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }
        else fileResume = null;
    }

    public static class ResumeViewItem {
        private final SimpleLongProperty id;
        private final SimpleStringProperty spec;
        private final SimpleIntegerProperty salary;
        private final SimpleStringProperty type;

        public ResumeViewItem(Long id, String spec, Integer aSalary, String type) {
            this.id = new SimpleLongProperty(id);
            this.spec = new SimpleStringProperty(spec);
            this.type = new SimpleStringProperty(type);
            this.salary = new SimpleIntegerProperty(aSalary);
        }

        public int getSalary() {
            return salary.get();
        }

        public SimpleIntegerProperty salaryProperty() {
            return salary;
        }

        public void setSalary(int salary) {
            this.salary.set(salary);
        }

       public long getId() {
            return id.get();
        }

        public SimpleLongProperty idProperty() {
            return id;
        }

        public void setId(long id) {
            this.id.set(id);
        }

        public String getSpec() {
            return spec.get();
        }

        public SimpleStringProperty specProperty() {
            return spec;
        }

        public void setSpec(String spec) {
            this.spec.set(spec);
        }

        public String getType() {
            return type.get();
        }

        public SimpleStringProperty typeProperty() {
            return type;
        }

        public void setType(String type) {
            this.type.set(type);
        }
    }


    private void refreshResumeTableView () {
        resumeListView.setPlaceholder(new Label("Нет данных"));
        final ObservableList<ResumeViewItem> tableList = FXCollections.observableArrayList();
        Resume it;
        for (Long id : resumes.keySet()) {
            it = resumes.get(id);
            tableList.add(new ResumeViewItem(it.getId(), it.getPosition(), it.getSalary(), it.getType()));

        }

        resumeTableIdCol.setCellValueFactory(new PropertyValueFactory<ResumeViewItem, Long>("id"));
        resumeTableSpecCol.setCellValueFactory(new PropertyValueFactory<ResumeViewItem, String>("spec"));
        resumeTableSalaryCol.setCellValueFactory(new PropertyValueFactory<ResumeViewItem, Integer>("salary"));
        resumeTableTypeCol.setCellValueFactory(new PropertyValueFactory<ResumeViewItem, String>("type"));
        resumeListView.setItems(tableList);
        resumeTableIdCol.setSortType(TableColumn.SortType.ASCENDING);

        resumeListView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<ResumeViewItem>() {
            @Override
            public void changed(ObservableValue<? extends ResumeViewItem> observableValue, ResumeViewItem Old, ResumeViewItem New) {
                if (New != null) {
                    curResumeId = New.getId();
                    currentResume = resumes.get(curResumeId);
                    fillResumeHistoryDetail(currentResume);
                    Thread newPreviewThread = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            List<Node> previews;
                            try {
                                previews = previewResume(currentResume);  //previewResume is synchronized method
                                if (previews != null) refreshPreviewResume(previews);
                            } catch (Exception e) {  // catch InterruptedException
                                  Thread.currentThread().interrupt();
                            }
                        }
                    });


                    if (prevPreviewThread != null) prevPreviewThread.interrupt();
                    prevPreviewThread = newPreviewThread;
                    newPreviewThread.start();

                }
            }
        });

        resumeListView.getSelectionModel().selectFirst();
    }

    private void refreshPreviewResume(Collection<Node> cont) {

        final Collection<Node> list = cont;

        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                resumePreviewPane.getChildren().clear();
                resumePreviewPane.getChildren().setAll(list);
            }
        });

    }

    private void refreshPreviewResume(Node cont) {

        final Node list = cont;

        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                resumePreviewPane.getChildren().clear();
                resumePreviewPane.getChildren().add(list);
            }
        });

    }


    synchronized private List<Node> previewResume(Resume currentResume) { // Не вызывать метод из потока приложения JavaFX!!!

        List<Node> previews = new ArrayList<>();
        final double previewWidth  = resumePreviewPane.getWidth()*0.9;
        final double previewHeight = previewWidth/0.7;  // A4 size

        setStatusbar("Обновление превью ...");

        if (currentResume != null) {
            if (currentResume.getType().equalsIgnoreCase("pdf")) {

// JPedal

                PdfDecoder pdf = new PdfDecoder();

                try {
                    pdf.openPdfArray(currentResume.getBody());
                } catch (PdfException e) {
                    previews = Arrays.asList(createBlankPreview(previewWidth, previewHeight, "Ошибка распознавания"));
                    setStatusbar("Превью обновлено");
                    return Thread.currentThread().isInterrupted() ? null : previews;
                }

                for (int i = 1; i <= pdf.getPageCount() && !Thread.currentThread().isInterrupted(); i++) {
                    int pW = pdf.getPdfPageData().getCropBoxWidth(i);
                    int pH = pdf.getPdfPageData().getCropBoxHeight(i);

                    try {
                        BufferedImage bImg = pdf.getPageAsImage(i);

//                        assert bImg != null : "Не удалось преобразование PDF->Image";

                        if (bImg == null) throw new PdfException();

                        WritableImage img = new WritableImage(bImg.getWidth(), bImg.getHeight());
                        SwingFXUtils.toFXImage(bImg, img);
                        ImageView view = new ImageView(img);
                        view.setFitWidth(previewWidth);
                        view.setPreserveRatio(true);
                        view.setCache(true);

                        view.setEffect(new DropShadow(3.0, 4.0, 4.0, Color.DARKGRAY));

                        previews.add(view);
                        previews.add(new Separator());
                    } catch (PdfException e) {
                        previews.add(createBlankPreview(previewWidth, previewHeight, "Ошибка"));
                    }
                }
                pdf.closePdfFile();

/*  // PDFBox
                InputStream in = new ByteArrayInputStream(currentResume.getBody());
                PDDocument pdf = null;

                try {
                     pdf = PDDocument.load(in);
                } catch (IOException e) {
                    refreshPreviewResume(createBlankPreview(previewWidth, previewHeight, "Ошибка распознавания"));
                    setStatusbar("Превью обновлено");
                    return;
                }
                List<PDPage> pages =  pdf.getDocumentCatalog().getAllPages();

                Iterator<PDPage> it = pages.iterator();
                List<Node> previews = new ArrayList<>();

                for (PDPage pg : pages) {
                    try {
//                        BufferedImage bImg = pg.convertToImage(BufferedImage.TYPE_3BYTE_BGR, 300);
                        BufferedImage bImg = pg.convertToImage();
                        WritableImage img = new WritableImage(bImg.getWidth(), bImg.getHeight());
                        SwingFXUtils.toFXImage(bImg, img);
                        ImageView view = new ImageView(img);
                        view.setFitWidth(previewWidth);
//                        view.fitWidthProperty().bind(new SimpleDoubleProperty(resumePreviewPane.widthProperty().doubleValue()));

                        view.setPreserveRatio(true);
                        view.setCache(true);
                        view.setSmooth(true);

                        view.setEffect(new DropShadow(3.0, 4.0, 4.0, Color.DARKGRAY));

                        previews.add(view);
                        previews.add(new Separator());
                    } catch (IOException e) {
                        previews.add(createBlankPreview(previewWidth, previewHeight, "Ошибка"));
                    }
                }


                try {
                    pdf.close();
                    in.close();
                } catch (IOException e) {
                    assert false : "Ошибка при закрытиии PDF";
                }

*/

            }
            else if (currentResume.getType().equalsIgnoreCase("doc") || currentResume.getType().equalsIgnoreCase("docx")) {
                //TODO добавить превью для DOC
                previews = null;
            }
            else previews = Arrays.asList(createBlankPreview(previewWidth, previewHeight, "Предпросмотр недоступен"));
        }
        setStatusbar("Превью обновлено");
        return Thread.currentThread().isInterrupted() ? null : previews;
    }

    private Node createBlankPreview(double previewWidth, double previewHeight, String s) {
        Rectangle field = new Rectangle(previewWidth, previewHeight, Color.WHITE);
        field.setStroke(Color.BLACK);
        field.setStrokeWidth(1.0);

        Text warn = new Text(s);
        warn.setFontSmoothingType(FontSmoothingType.LCD);
        warn.setWrappingWidth(previewWidth * 0.8);
        warn.setTextAlignment(TextAlignment.CENTER);
        warn.setFont(Font.getDefault());
        warn.setX(previewWidth*0.1);
        warn.setY(previewHeight/2);

        Group gr = new Group(field, warn);
        gr.setEffect(new DropShadow(3.0, 4.0, 4.0, Color.DARKGRAY));

        return gr;
    }

    private void fillResumeHistoryDetail(Resume resume) {

        if (resume == null) return;
        List<resumeTracker> tracker = history.get(resume.getId());

        currentResumeHistoryList.getItems().clear();
        historyContactDetailText.clear();
        if (tracker == null) {
            currentTrackerList = null;
            currentTracker = null;
            return;
        }
        currentTrackerList = new TreeMap<>();

        ObservableList<resumeTracker> list = FXCollections.observableArrayList();
        for (resumeTracker tr : tracker) {
            list.add(tr);
            currentTrackerList.put(tr.getId(), tr);
        }
        currentResumeHistoryList.getItems().setAll(list);


        currentResumeHistoryList.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<resumeTracker>() {
            @Override
            public void changed(ObservableValue<? extends resumeTracker> observableValue, resumeTracker t, resumeTracker t2) {
                    fillHistoryDetail(t2);
            }
        });
        currentResumeHistoryList.getSelectionModel().selectFirst();
    }

    private void fillHistoryDetail(resumeTracker t) {
        historyContactDetailText.clear();
        currentTracker = t;

        if (currentTracker != null) {
            historyContactDetailText.clear();
            Contact ct = t.getEmployer();
            if (ct == null) return;
            historyContactDetailText.appendText("Имя: " + ct.getName() + "\nОрганизация: " +
                                                                       ct.getOrganization() + "\nАдрес: " +
                                                                        ct.getAddress() + "\nТелефон: " +
                                                                        ct.getPhones() + " E-mail: " + ct.getEmails() + "\nКомментарий" +
                                                                        ct.getComment() + "\n---------------------------\n");
            historyContactDetailText.appendText("Следущий звонок: " + currentTracker.getNextRegularCall() + "\n");

            String status =  currentTracker.getStatus().toString();

//            switch (currentTracker.getStatus()) {
//                case UNCHECKED:
//                    status = "Проверить получение резюме работадателем";
//                    break;
//                case CHECKED:
//                    status = "Есть подтверждение получения, звонить, уточнить результаты";
//                    break;
//                case DELAYED:
//                    status = "Рассмотрение резюме отложено";
//                    break;
//                case RESERVED:
//                    status = "Резюме ушло в резерв";
//                    break;
//                case UNKNOWN:
//                    status = "Состояние неизвестно, уточнить";
//                    break;
//                case CONSIDERED:
//                    status = "Еще обдумывают";
//                    break;
//                case REJECTED:
//                    status = "Отказ, больше не звонить";
//                    break;
//                case ACCEPTED:
//                    status = "Приглашены на собеседование";
//                    break;
//            }
//
//
            historyContactDetailText.appendText("Статус: " + status + "\n");
            historyContactDetailText.appendText("Комментарий к отправке: " + currentTracker.getComment());

        }
    }

    private List<Contact> findContact(String name, String org) {
        List<Contact> foundContact = new LinkedList<>();
        for (Map.Entry<Long, Contact> ct : catalog.entrySet())  //TODO изменить на запрос к базе данных
            if (ct.getValue().getName().equalsIgnoreCase(name) && ct.getValue().getOrganization().equalsIgnoreCase(org)) {
                foundContact.add(ct.getValue());
            }
        return foundContact.size() > 0 ? foundContact : null;
    }


    @FXML
    private void addResume(ActionEvent event) {

        addResumeStage.setTitle("Добавление нового резюме");
        buttonAddResume.setText("Добавить");
        buttonAddResume.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                addResumeDoAction(actionEvent);
            }
        });
        fileUpdateFlag.setVisible(false);
        addResumeStage.show();

    }
    @FXML
    private void closeAddResumeWindow() {

        addResumeStage.hide();

    }


    @FXML
    private void deleteResume(ActionEvent event) {
        Dialogs dg = Dialogs.create();
        dg.owner(stage);
        dg.title("Запрос подтверждения");
        dg.masthead("Вы собираетесь удалить резюме.  Выполнить?");
        dg.message("История также будет удалена");

        Action response = dg.showConfirm();
        if (response == Dialog.Actions.YES) {
            if (currentResume != null) {
                long id = currentResume.getId();
                if (deleteResumeFromDatabase(currentResume, stage)) {
                        int indx = resumeListView.getSelectionModel().getSelectedIndex();

                        assert indx >= 0 : "Всегда должно быть выбранное резюме";

                        resumeListView.getItems().remove(indx);
                        resumes.remove(id);
                        resumeListView.getSelectionModel().select(indx);
                }
            }
        }

    }

    private boolean deleteResumeFromDatabase(Resume resume, Stage stage) {
        if (resume == null) return false;

        boolean success = true;

        PersistenceManager pm = pmf.getPersistenceManager();

        setStatusbar("Удаление из базы данных");

        Transaction tx = pm.currentTransaction();
        try {

            tx.begin();

            List<resumeTracker> tracker = history.get(resume.getId());
            if (tracker != null) {
                for (resumeTracker tr : tracker) {
                    pm.deletePersistent(tr);
                }
            }
            pm.deletePersistent(resume);
            tx.commit();
        }
        finally {
            if (tx.isActive())
            {
                tx.rollback();

                Dialogs dg = Dialogs.create();
                dg.owner(stage);
                dg.title("Ошибка доступа к базе данных");
                dg.masthead("Не удается удалить данные");
                dg.message("Попробуйте еще раз");

                Action response = dg.showError();

                success = false;

            }
            pm.close();
            setStatusbar("Завершено");
        }

        return success;
    }

    private boolean deleteResumeAllHistoryEntry(Resume resume) {

        if (resume != null) {
            List<resumeTracker> tracker = history.get(resume.getId());
            if (tracker == null) return true;
            for (resumeTracker tr : tracker) {
                if (deleteObjectFromDatabase(tr, stage)) {
                    history.get(resume.getId()).remove(tr);
                }
            }
            if (history.get(resume.getId()).size() == 0) {
                history.remove(resume.getId());
                return true;
            }
            else return false;
        }
        return false;
    }

    private boolean deleteObjectFromDatabase(Object o, Stage ownerWindow) {

        if (o == null) return false;

        boolean success = true;

        PersistenceManager pm = pmf.getPersistenceManager();

        setStatusbar("Удаление из базы данных");

        Transaction tx = pm.currentTransaction();
        try {

            tx.begin();

            pm.deletePersistent(o);
            tx.commit();
        }
        finally {
            if (tx.isActive())
            {
                tx.rollback();

                Dialogs dg = Dialogs.create();
                dg.owner(ownerWindow);
                dg.title("Ошибка доступа к базе данных");
                dg.masthead("Не удается удалить данные");
                dg.message("Попробуйте еще раз");

                Action response = dg.showError();

                success = false;

            }
            pm.close();
            setStatusbar("Завершено");
        }

        return success;
    }

    @FXML
    private void openResumeInSystemBrowser(ActionEvent event) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                if (currentResume == null) return;
                try {
                    pipe.put(new pipeMessage(currentResume.getBody(), currentResume.getType()));
                } catch (InterruptedException e) {
                    assert false : "Прервано при добавлении задания в pipe";
                }
            }
        }).start();
    }

    @FXML
    private void registerResumeSent(ActionEvent event) {

        if (currentResume == null) return;
        fillSentResumeContactInfo(currentResume.getGivenName(), currentResume. getPosition(), currentResume.getGivenPhones(), currentResume.getGivenEmails());
        sentResumeDialog.show();
    }
    @FXML
    private void showResumeHistoryStatus(ActionEvent event) {

        if (currentTrackerList == null || currentTracker == null) return;
        fillShowResumeStatusInfo();
        showResumeStatusDialog.show();
    }

    private void fillShowResumeStatusInfo() {
        if (currentTracker == null) return;
        Contact ct = currentTracker.getEmployer();
        employerInfo.setText("Имя: " + ct.getName() + " Организация: " +
                ct.getOrganization() + "\nАдрес: " +
                ct.getAddress() + "\nТелефон: " +
                ct.getPhones() + " E-mail: " + ct.getEmails() + "\nКомментарий" +
                ct.getComment());
        commentArea.setText(currentTracker.getComment());

        if (currentTracker.getSentDate() != null) sentDateField.setValue(new Date(currentTracker.getSentDate().getTimeInMillis()));
        if (currentTracker.getLastCall() != null) lastCallField.setValue(new Date(currentTracker.getLastCall().getTimeInMillis()));
        if (currentTracker.getDateOfInterview() != null) interviewDateField.setValue(new Date(currentTracker.getDateOfInterview().getTimeInMillis()));
        statusField.getItems().clear();
        statusField.getItems().addAll(resumeStatus.values());
        statusField.setValue(currentTracker.getStatus());
    }

    private java.time.LocalDate toLocalDate(GregorianCalendar date) {
        int year = date.get(GregorianCalendar.YEAR);
        int month = date.get(GregorianCalendar.MONTH);
        int day = date.get(GregorianCalendar.DAY_OF_MONTH);
        return java.time.LocalDate.of(year, month, day);
    }

    @FXML
    private void resetSentResume(ActionEvent event) {

        if (currentTrackerList == null || currentTracker == null) return;
        resumeTracker tr = currentTracker;
        if (deleteObjectFromDatabase(tr, stage)) {
            history.get(currentResume.getId()).remove(tr);
            if (history.get(currentResume.getId()).size() == 0) history.remove(currentResume.getId());
            fillResumeHistoryDetail(currentResume);
        }
    }


    @FXML
    private void updateResume(ActionEvent event) {

        if (currentResume == null) return;

        addResumeStage.setTitle("Обновление резюме");
        buttonAddResume.setText("Обновить");
        addResumeSpec.setText(currentResume.getPosition());
        addResumeSalary.setText(((Integer)currentResume.getSalary()).toString());
        buttonAddResume.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                updateResumeDoAction(actionEvent);
            }
        });
        fileUpdateFlag.setVisible(true);
        addResumeStage.show();
    }

    private void updateResumeDoAction(ActionEvent actionEvent) {
        if (fileUpdateFlag.isSelected() && fileResume == null) {

            Dialogs dg = Dialogs.create();
            dg.owner(addResumeStage);
            dg.title("Ошибка при обновлении резюме");
            dg.masthead("Нужно выбрать файл резюме");

            Action response = dg.showError();
            return;
        }
        if (addResumeContactTableView.getSelectionModel().getSelectedItem() == null) {
            Dialogs dg = Dialogs.create();
            dg.owner(addResumeStage);
            dg.title("Ошибка при обновлении резюме");
            dg.masthead("Нужно выбрать контактные данные для резюме");

            Action response = dg.showError();
            return;

        }
        if (addResumeSalary.getText().equals("")) addResumeSalary.setText("0");

        Integer salary;
        try {
            salary = Integer.parseInt(addResumeSalary.getText());
        } catch (NumberFormatException e) {
            Dialogs dg = Dialogs.create();
            dg.owner(addResumeStage);
            dg.title("Ошибка при обновлении резюме");
            dg.masthead("Значение заработной платы должно быть числом.  Ноль означает - \"Зарплата по договоренности\"");

            Action response = dg.showError();
            return;
        }

        if (fileUpdateFlag.isSelected()) setStatusbar("Чтение файла");

        try {
            assert addResumeContactTableView.getSelectionModel().getSelectedItem() != null : "Всегда должны быть выбраны контактные данные";

            Contact ct = catalog.get(addResumeContactTableView.getSelectionModel().getSelectedItem().getId());

            assert ct != null : "Выбранный контакт должен существовать в каталоге";

            currentResume.setSalary(salary);
            currentResume.setPosition(addResumeSpec.getText());
            currentResume.setGivenName(ct.getName());
            currentResume.setGivenPhones(ct.getPhones());
            currentResume.setGetGivenEmails(ct.getEmails());
            if (fileUpdateFlag.isSelected()) {
                currentResume.setBody(fileResume);
                setStatusbar("Файл прочитан");
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        previewResume(currentResume);
                    }
                }).start();
            }
        } catch (IOException e) {
            Dialogs dg = Dialogs.create();
            dg.owner(addResumeStage);
            dg.title("Ошибка при обновлении резюме");
            dg.masthead("Сбой при выполении файловой операции при чтении");
            dg.message("Попробуйте еще раз");

            Action response = dg.showError();
            setStatusbar("Ошибка при чтении файла");
        }

        if (currentResume != null)
            if (saveResume(currentResume)) {
                resumes.put(currentResume.getId(), currentResume);
                resumeListView.getSelectionModel().getSelectedItem().setSalary(currentResume.getSalary());
                resumeListView.getSelectionModel().getSelectedItem().setSpec(currentResume.getPosition());
                resumeListView.getSelectionModel().getSelectedItem().setId(currentResume.getId());
                resumeListView.getSelectionModel().getSelectedItem().setType(currentResume.getType());
                setStatusbar("Резюме обновлено");
                closeAddResumeWindow();
            }
            else setStatusbar("Ошибка базы данных при добавлении резюме");
    }


    @FXML
    private void deteleContact(ActionEvent event) {

        assert currentContact != null : "Текущий контакт всегда должен быть установлен";

        long id = currentContact.getId();

        if (mine.getContact().getId() == id) {
            setStatusbar("Собственные контактные данные удалить нельзя");
            return;
        }

        if (deleteContactFromDatabase(currentContact)) {

            int tblItemIndex = emplTable.getSelectionModel().getSelectedIndex();

            assert tblItemIndex >= 0 : "Всегда должен быть выбранный элемент в таблице";

            emplTable.getItems().remove(tblItemIndex);

//            setStatusbar(""+tblItemIndex);

            catalog.remove(id);
            emplTable.getSelectionModel().select(tblItemIndex);

        }
    }

    private boolean deleteContactFromDatabase(Contact contact) {
        boolean success = true;

        setStatusbar("Удаление записи из базы данных");

        PersistenceManager pm = pmf.getPersistenceManager();

        Transaction tx = pm.currentTransaction();
        try {

            tx.begin();
            pm.deletePersistent(contact);
            tx.commit();
        }
        finally {
            if (tx.isActive())
            {
                tx.rollback();

                Dialogs dg = Dialogs.create();
                dg.owner(stage);
                dg.title("Ошибка доступа к базе данных");
                dg.masthead("Не удается удалить данные");
                dg.message("Попробуйте еще раз");

                Action response = dg.showError();

                success = false;

            }
            pm.close();
            setStatusbar("Завершено");
        }

        return success;
    }

    @FXML
    private void clearDetailContact(ActionEvent event) {
        fillContactDetail(new Contact());
    }

    @FXML
    private void saveDetailContact(ActionEvent event) {
        if (!currentContact.getId().equals(mine.getContact().getId())) {
            currentContact.setName(detName.getText());
            currentContact.setAddress(detAddress.getText());
            currentContact.setEmails(detEmail.getText());
            currentContact.setPhones(detPhone.getText());
            currentContact.setComment(detComment.getText());
            currentContact.setOrganization(detOrganization.getText());
            currentContact.setPersonFlag(!detOrganizationFlag.isSelected());

            saveContact(currentContact);
            curContactId = currentContact.getId();

            emplTable.getSelectionModel().getSelectedItem().setName(currentContact.getName());
            emplTable.getSelectionModel().getSelectedItem().setOrganization(currentContact.getOrganization());
            emplTable.getSelectionModel().getSelectedItem().setId(curContactId);
            setStatusbar("Обновлено");
        }
        else setStatusbar("Не обновлено.  Используйте форму 'Мои данные'");

    }

    @FXML
    private void newContact(ActionEvent event) {

        Contact newCt = new Contact(detName.getText(),detAddress.getText(),detOrganization.getText(),detPhone.getText(),detEmail.getText(),!detOrganizationFlag.isSelected());
        newCt.setComment(detComment.getText());

        if (saveContact(newCt)) {
            catalog.put(newCt.getId(), newCt);
            currentContact = newCt;
            curContactId = currentContact.getId();
            ContactTableItem tblcont = new ContactTableItem(newCt.getId(), newCt.getName(), newCt.getOrganization(), newCt.getPhones(), newCt.getEmails());
            emplTable.getItems().add(tblcont);
            emplTable.getSelectionModel().select(tblcont);
        }
    }

    private boolean saveContact(Contact newCt) {
        return saveObject(newCt, stage);
    }

    @FXML
    private void contactDataChanged() {

        changed = true;
        setTabMyDataLabel(changed);

    }
    @FXML
    private void tabSelectChanged(Event event) {

        if (changed) {
            Dialogs dg = Dialogs.create();
            dg.owner(stage);
            dg.title("Данные изменены");
            dg.masthead("Вы не сохранили измененные данные");
            dg.message("Сохранить?");

            Action response = dg.showConfirm();

            if (response == Dialog.Actions.OK || response == Dialog.Actions.YES) {
                setMyContact();
            }
            else if (response != Dialog.Actions.CANCEL) {
                changed = false;
                setMyFormContact();
                setTabMyDataLabel(changed);
            }
            else {
                event.consume();
            }

        }
    }

    @FXML
    private void setMyFormContact () {

        if (!changed) {
            myName.setText(mine.getContact().getName());
            myAddress.setText(mine.getContact().getAddress());
            myPhone.setText(mine.getContact().getPhones());
            myEmail.setText(mine.getContact().getEmails());
        }
    }

    private void setTabMyDataLabel(boolean flag) {
        if (flag) {
            if (tabMyData.getText().lastIndexOf('*')<0) tabMyData.setText(tabMyData.getText()+"*");
        }
        else {
            if (tabMyData.getText().lastIndexOf('*')>0) tabMyData.setText(tabMyData.getText().substring(0,tabMyData.getText().lastIndexOf('*')));
        }
    }
    @FXML
    private void setMyContact () {
        PersistenceManager pm = pmf.getPersistenceManager();

        Transaction tx = pm.currentTransaction();
        try {

            tx.begin();

            setStatusbar("Сохранение");

//            mine.setContact(new Contact(myName.getText(),myAddress.getText(),"",myPhone.getText(),myEmail.getText(),true));

            mine.getContact().setName(myName.getText());
            mine.getContact().setAddress(myAddress.getText());
            mine.getContact().setPhones(myPhone.getText());
            mine.getContact().setEmails(myEmail.getText());

            pm.makePersistent(mine);

            tx.commit();
            changed = false;
            setTabMyDataLabel(changed);
            catalog.put(mine.getContact().getId(), mine.getContact());
            refreshContactTableView();
        }
        finally {
            if (tx.isActive())
            {
                tx.rollback();

                Dialogs dg = Dialogs.create();
                dg.owner(stage);
                dg.title("Ошибка доступа к базе данных");
                dg.masthead("Не удается сохранить данные");
                dg.message("Попробуйте еще раз");

                Action response = dg.showError();


            }
            pm.close();
            setStatusbar("Завершено");
        }
    }

    @FXML
    private void saveDB() {
        saveObjects(false);
    }

    @FXML
    private void resumedbQuit() {
        if (saveObjects(true)) {
            exitWithoutSave();
        }
    }
    @FXML
    private void exitWithoutSave() {
            pmf.close();
            pipe.offer(new pipeMessage());
            Platform.exit();
    }

    @FXML
    private void clearMyContact () {
        myName.setText("");
        myAddress.setText("");
        myPhone.setText("");
        myEmail.setText("");
        contactDataChanged();
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {

/*
        assert DetailLabel != null : "fx:id=\"DetailLabel\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert currentResumeHistoryList != null : "fx:id=\"currentResumeHistoryList\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert detAddress != null : "fx:id=\"detAddress\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert detComment != null : "fx:id=\"detComment\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert detEmail != null : "fx:id=\"detEmail\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert detName != null : "fx:id=\"detName\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert detOrganization != null : "fx:id=\"detOrganization\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert detOrganizationFlag != null : "fx:id=\"detOrganizationFlag\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert detPhone != null : "fx:id=\"detPhone\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert emplTable != null : "fx:id=\"emplTable\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert emplTableIdCol != null : "fx:id=\"emplTableIdCol\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert emplTableNameCol != null : "fx:id=\"emplTableNameCol\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert emplTableOrgCol != null : "fx:id=\"emplTableOrgCol\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert historyContactDetailText != null : "fx:id=\"historyContactDetailText\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert myAddress != null : "fx:id=\"myAddress\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert myEmail != null : "fx:id=\"myEmail\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert myName != null : "fx:id=\"myName\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert myPhone != null : "fx:id=\"myPhone\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert resumeInfoLabel != null : "fx:id=\"resumeInfoLabel\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert resumeListView != null : "fx:id=\"resumeListView\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert resumePreviewPane != null : "fx:id=\"resumePreviewPane\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert statusbar != null : "fx:id=\"statusbar\" was not injected: check your FXML file 'startFrame.fxml'.";
        assert tabMyData != null : "fx:id=\"tabMyData\" was not injected: check your FXML file 'startFrame.fxml'.";
*/

    }


    private PersistenceManagerFactory getPmf() {
        return pmf;
    }

    private boolean saveObjects(boolean wait) {

        Task<Boolean> ft = new Task<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                Boolean success = true;
                PersistenceManager pm = pmf.getPersistenceManager();

                Transaction tx = pm.currentTransaction();
                try {
                    setStatusbar("Сохранение");
                    tx.begin();
                    if (!isCancelled()) pm.makePersistent(mine);
                    tx.commit();
                    tx.begin();
                    for (Long id : catalog.keySet()) {
                        if (isCancelled()) break;
                        pm.makePersistent(catalog.get(id));
                    }
                    if (!isCancelled()) {
                        tx.commit();

                        for (Long r : resumes.keySet()) {
                            if (isCancelled()) break;
                            tx.begin();
                            pm.makePersistent(resumes.get(r));
                            tx.commit();
                            List<resumeTracker> listTr = history.get(resumes.get(r).getId());
                            if (listTr != null)
                                for (resumeTracker tr : listTr) {
                                    tx.begin();
                                    pm.makePersistent(tr);
                                    tx.commit();
                                }
                        }
                    }
                }
                finally {
                    if (tx.isActive())
                    {
                        tx.rollback();

                        if (!isCancelled()) {
                            Dialogs dg = Dialogs.create();
                            dg.owner(stage);
                            dg.title("Ошибка доступа к базе данных");
                            dg.masthead("Не удается сохранить данные");
                            dg.message("Попробуйте еще раз");

                            Action response = dg.showError();
                        }

                        success = false;

                    }
                    pm.close();
                }

                return success;
            }
            @Override protected void succeeded() {
                super.succeeded();
                setStatusbar("Сохранение завершено");
            }

            @Override protected void cancelled() {
                super.cancelled();
                setStatusbar("Операция прервана");
            }

            @Override protected void failed() {
                super.failed();
                setStatusbar("Операция не выполнена");
            }


        };

        new Thread(ft).start();

        if (wait) {
            try {
                return ft.get();
            } catch (InterruptedException e) {
                return false;
            } catch (ExecutionException e) {
                return false;
            }
        } else return false;
    }

    public void setStage(Stage stage, MainFrameFXMLController controller, Stage addResumeStage, Stage sentResumeDialog, Stage showResumeStatusDialog) {
        this.stage = stage;
        this.controller = controller;
        this.addResumeStage = addResumeStage;
        this.sentResumeDialog = sentResumeDialog;
        this.showResumeStatusDialog = showResumeStatusDialog;
    }

    public void setJDO(PersistenceManagerFactory pmf, Mine mine, ConcurrentMap<Long,
            Contact> catalog, ConcurrentMap<Long, Resume> resumes, ConcurrentMap<Long, List<resumeTracker>> history, BlockingQueue<pipeMessage> pipe) {

        this.pmf = pmf;
        this.mine = mine;
        this.catalog = catalog;
        this.resumes = resumes;
        this.history = history;
        this.pipe = pipe;

        setViewsAndControls();

    }



    private void setViewsAndControls() {

        stage.addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent windowEvent) {
                if (!saveObjects(true)) windowEvent.consume();
                else getPmf().close();
                pipe.offer(new pipeMessage());
            }
        });

        myName.setText(mine.getContact().getName());
        myAddress.setText(mine.getContact().getAddress());
        myPhone.setText(mine.getContact().getPhones());
        myEmail.setText(mine.getContact().getEmails());

        refreshAllTableView();

    }

    private void refreshAllTableView() {
        refreshContactTableView();
        refreshResumeTableView();
        refreshAddResumeContactTableView();
        refreshSentResumeContactTable();
    }

    private void refreshSentResumeContactTable() {
        final ObservableList<ContactTableItem> tableList;
        if (emplTable.getItems() != null) {
            tableList = emplTable.getItems();
        }
        else {
            tableList = FXCollections.observableArrayList();
            Contact itContact;
            for (Long id : catalog.keySet()) {
                itContact = catalog.get(id);
                tableList.add(new ContactTableItem(itContact.getId(), itContact.getName(), itContact.getOrganization(), itContact.getPhones(), itContact.getEmails()));

            }
        }

        sentResumeIdCol.setCellValueFactory(new PropertyValueFactory<ContactTableItem, Long>("id"));
        sentResumeNameCol.setCellValueFactory(new PropertyValueFactory<ContactTableItem, String>("name"));
        sentResumePhoneCol.setCellValueFactory(new PropertyValueFactory<ContactTableItem, String>("phones"));
        sentResumeEmailCol.setCellValueFactory(new PropertyValueFactory<ContactTableItem, String>("emails"));

        sentResumeContactTableView.setItems(tableList);

//        sentResumeContactTableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<ContactTableItem>() {
//            @Override
//            public void changed(ObservableValue<? extends ContactTableItem> observableValue, ContactTableItem Old, ContactTableItem New) {
//                if (New != null) {
//                    fillAddResumeContactInfo(catalog.get(New.getId()));
//                }
//            }
//        });

        sentResumeContactTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
//        sentResumeContactTableView.getSelectionModel().selectFirst();
    }

    private void refreshAddResumeContactTableView() {

        final ObservableList<ContactTableItem> tableList;
        if (emplTable.getItems() != null) {
            tableList = emplTable.getItems();
        }
        else {
            tableList = FXCollections.observableArrayList();
            Contact itContact;
            for (Long id : catalog.keySet()) {
                itContact = catalog.get(id);
                tableList.add(new ContactTableItem(itContact.getId(), itContact.getName(), itContact.getOrganization(), itContact.getPhones(), itContact.getEmails()));

            }
        }

        addResumeIdCol.setCellValueFactory(new PropertyValueFactory<ContactTableItem, Long>("id"));
        addResumeNameCol.setCellValueFactory(new PropertyValueFactory<ContactTableItem, String>("name"));
        addResumePhoneCol.setCellValueFactory(new PropertyValueFactory<ContactTableItem, String>("phones"));
        addResumeEmailCol.setCellValueFactory(new PropertyValueFactory<ContactTableItem, String>("emails"));

        addResumeContactTableView.setItems(tableList);

        addResumeContactTableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<ContactTableItem>() {
            @Override
            public void changed(ObservableValue<? extends ContactTableItem> observableValue, ContactTableItem Old, ContactTableItem New) {
                if (New != null) {
                    fillAddResumeContactInfo(catalog.get(New.getId()));
                }
            }
        });

        addResumeContactTableView.getSelectionModel().selectFirst();
    }

    private void fillAddResumeContactInfo(Contact contact) {
        if (contact != null)
            addResumeContactInfo.setText("Контактные данные в резюме\n" +
                (contact.getName().equals("") ? "" : ("Имя: " + contact.getName() + "\n")) +
                (contact.getOrganization().equals("") ? "" : ("Организация: " + contact.getOrganization() + "\n")) +
                (contact.getPhones().equals("") ? "" : ("Телефон: " + contact.getPhones() + "\n")) +
                (contact.getEmails().equals("") ? "" : ("E-mail: " + contact.getEmails())));
        else addResumeContactInfo.setText("");
    }

    private void fillSentResumeContactInfo(String name, String pos, String phones, String emails) {
        if (name != null && pos != null && phones != null && emails != null) {
            ResumeContactInfo.setText(
                    (name.equals("") ? "" : ("Имя: " + name + "\n")) +
                    (pos.equals("") ? "" : ("Позиция: " + pos + "\n")) +
                    (phones.equals("") ? "" : ("Телефон: " + phones + " ")) +
                    (emails.equals("") ? "" : ("E-mail: " + emails)));
        }
        else ResumeContactInfo.setText("");
    }


    private void refreshContactTableView() {
        emplTable.setPlaceholder(new Label("Нет данных"));
        final ObservableList<ContactTableItem> tableList = FXCollections.observableArrayList();
        Contact itContact;
        for (Long id : catalog.keySet()) {
            itContact = catalog.get(id);
            tableList.add(new ContactTableItem(itContact.getId(), itContact.getName(), itContact.getOrganization(), itContact.getPhones(), itContact.getEmails()));

        }

        emplTableIdCol.setCellValueFactory(new PropertyValueFactory<ContactTableItem, Long>("id"));
        emplTableNameCol.setCellValueFactory(new PropertyValueFactory<ContactTableItem, String>("name"));
        emplTableOrgCol.setCellValueFactory(new PropertyValueFactory<ContactTableItem, String>("organization"));
        emplTable.setItems(tableList);

        emplTable.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<ContactTableItem>() {
            @Override
            public void changed(ObservableValue<? extends ContactTableItem> observableValue, ContactTableItem contactTableItemOld, ContactTableItem contactTableItemNew) {
                if (contactTableItemNew != null) {
                    curContactId = contactTableItemNew.getId();
                    currentContact = catalog.get(curContactId);
                    fillContactDetail(currentContact);
                }
            }
        });

        emplTable.getSelectionModel().selectFirst();
//        curContactId = emplTable.getSelectionModel().getSelectedItem().getId();
//        currentContact = catalog.get(curContactId);

    }

    private void fillContactDetail(Contact contact) {

        if (contact != null) {
            detName.setText(contact.getName());
            detAddress.setText(contact.getAddress());
            detEmail.setText(contact.getEmails());
            detPhone.setText(contact.getPhones());
            detOrganization.setText(contact.getOrganization());
            detOrganizationFlag.setSelected(!contact.isPerson());
            detComment.setText(contact.getComment());
            DetailLabel.setText("Детали (id: "+contact.getId()+")");
        }
    }

    public static class ContactTableItem {

        private final SimpleLongProperty id;
        private final SimpleStringProperty name;
        private final SimpleStringProperty organization;
        private final SimpleStringProperty phones;
        private final SimpleStringProperty emails;

        public String getPhones() {
            return phones.get();
        }

        public SimpleStringProperty phonesProperty() {
            return phones;
        }

        public void setPhones(String phones) {
            this.phones.set(phones);
        }

        public String getEmails() {
            return emails.get();
        }

        public SimpleStringProperty emailsProperty() {
            return emails;
        }

        public void setEmails(String emails) {
            this.emails.set(emails);
        }

        private ContactTableItem(Long id, String name, String organization, String phones, String emails) {
            this.id = new SimpleLongProperty(id);
            this.name = new SimpleStringProperty(name);
            this.organization = new SimpleStringProperty(organization);
            this.phones = new SimpleStringProperty(phones);
            this.emails = new SimpleStringProperty(emails);
        }

        public long getId() {
            return id.get();
        }

        public SimpleLongProperty idProperty() {
            return id;
        }

        public void setId(long id) {
            this.id.set(id);
        }

        public String getName() {
            return name.get();
        }

        public SimpleStringProperty nameProperty() {
            return name;
        }

        public void setName(String name) {
            this.name.set(name);
        }

        public String getOrganization() {
            return organization.get();
        }

        public SimpleStringProperty organizationProperty() {
            return organization;
        }

        public void setOrganization(String organization) {
            this.organization.set(organization);
        }

    }

    public void setStatusbar(String message) {

        class SetStatusClass implements Runnable {

            private String message;

            @Override
            public void run() {
                statusbar.setText(message);
            }
            SetStatusClass(String aMessage) {
                message = aMessage;
            }
        }


        Platform.runLater(new SetStatusClass(message));

    }

    private void clearStatusbar () {
        this.statusbar.setText("");
    }
}